package goutils

import (
	"crypto/md5"
	"encoding/hex"
	"go-micro-framework/go_service/core/goredis"
	"go-micro.dev/v4/logger"
	"math/rand"
	"strconv"
	"time"
)

const (
	REDIS_ACCESS_TOKE   = "access_token_"
	REDIS_REFRESH_TOKEN = "refresh_token_"
	// 定义字符集
	GENERATE_TOKEN = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
	EXPIRES_TIME   = 24 * time.Hour
)

type Token struct {
	UserId       int64             `json:"userId"`       // "用户id"
	Issuer       string            `json:"issuer"`       //平台账号申请者,或发行者
	AccessToken  string            `json:"accessToken"`  //请求业务token;
	RefreshToken string            `json:"refreshToken"` //刷新token
	TokenType    string            `json:"tokenType"`    //tokenType类型,暂时无用
	Expiry       time.Time         `json:"expiry"`       // AccessToken 过期时间;由时间转int64
	Created      time.Time         `json:"created"`      //创建时间,由时间转int64
	Scope        string            `json:"scope"`        //Scope参数
	Metadata     map[string]string `json:"metadata"`     //作用
	RedirectUri  string            `json:"redirectUri"`  //服务器回调地址
	ExtOpnId     string            `json:"extOpnId"`     //第三方扩展id
}

// Expired returns a boolean indicating if the token needs to be refreshed
func (t *Token) Expired() bool {
	return t.Expiry.Unix() < time.Now().Unix()
}

func (b *Token) setUserTokenInfo(userId int64, auth OAuthIssuer) bool {
	var expiresTime = 24 * time.Hour
	b.UserId = userId
	b.AccessToken = auth.GetAccessToken()
	b.RefreshToken = auth.GetRefreshToken()
	b.Created = time.Now()
	b.Expiry = time.Now().Add(expiresTime)
	return true
}
func (b *Token) SetUserTokenMapCache() bool {
	//REDIS_ACCESS_TOKE 有交时间为一天
	if b.UserId == 0 {
		logger.Error("SetUserTokenMapCache in userId is 0,please userId >0 ")
		return false
	}
	var userIds = strconv.FormatInt(b.UserId, 10)
	var oauth = NewOAuthIssuer()
	b.setUserTokenInfo(b.UserId, oauth)
	//var expiresTime = 24 * time.Hour
	//把时间转换整型保存到redis
	keyValue := make(map[string]any, 0)
	keyValue[b.AccessToken] = b.Expiry.Unix()
	var mapPrefixKey = REDIS_ACCESS_TOKE + userIds
	var result = goredis.NewRedisApi().HMSet(mapPrefixKey, keyValue)
	if result == nil || result.Err() != nil {
		return false
	} // 使用EXPIRE命令更新键的过期时间,过期时间为3天
	goredis.NewRedisApi().Expire(mapPrefixKey, 3*EXPIRES_TIME).Result()
	//设置刷新token
	var refreshKey = REDIS_REFRESH_TOKEN + userIds
	var refreshToken = goredis.NewRedisApi().Get(refreshKey)
	if refreshToken != nil && refreshToken.Val() != "" {
		b.RefreshToken = refreshToken.Val()
	}
	//保存或更新刷token,缓存时间为3天
	goredis.NewRedisApi().SetEx(refreshKey, b.RefreshToken, 3*EXPIRES_TIME)
	return true
}

func (b *Token) ModifyUserPassword() {
	var expiresTime = 1 * time.Second
	var userIds = strconv.FormatInt(b.UserId, 10)
	var mapPrefixKey = REDIS_ACCESS_TOKE + userIds
	var refreshKey = REDIS_REFRESH_TOKEN + userIds
	goredis.NewRedisApi().Expire(mapPrefixKey, expiresTime).Result()
	goredis.NewRedisApi().Del(refreshKey)
}

func (b *Token) GetUserTokenMapCache() int {
	var expiresTime = time.Now().Unix()
	var mapPrefixKey = REDIS_ACCESS_TOKE + strconv.FormatInt(b.UserId, 10)
	var mapKeyValue, err = goredis.NewRedisApi().GetMapCacheAndDelExpire(mapPrefixKey, expiresTime)
	if err != nil {
		return 1
	}
	if mapKeyValue == nil || len(mapKeyValue) == 0 {
		return 2
	}
	//key 不存在时, 表示token不存在,表示业务不正常
	if mapKeyValue[b.AccessToken] == 0 {
		return 3
	}
	//存在正常的token,表示业务正常
	return 0
}

type OAuthIssuer interface {
	GetAccessToken() string
	GetRefreshToken() string
}

type OAuthIssuerImpl struct {
	AccessToken  func() string
	RefreshToken func() string
}

func NewOAuthIssuer() OAuthIssuer {
	return &OAuthIssuerImpl{
		AccessToken:  generateToken,
		RefreshToken: generateToken,
	}
}

func (issuer *OAuthIssuerImpl) GetAccessToken() string {
	return issuer.AccessToken()
}

func (issuer *OAuthIssuerImpl) GetRefreshToken() string {
	return issuer.RefreshToken()
}
func generateToken() string {
	return GenerateToken(16)
}
func GenerateToken(length int) string {
	rand.NewSource(time.Now().UnixNano())
	// 定义字符集
	charset := GENERATE_TOKEN
	// 生成随机字符串
	randomToken := make([]byte, length)
	for i := 0; i < length; i++ {
		randomToken[i] = charset[rand.Intn(len(charset))]
	}
	// 使用MD5哈希算法生成摘要
	hash := md5.Sum(randomToken)
	// 将摘要转换为字符串并返回
	var token = hex.EncodeToString(hash[:])
	return token
}
